package common

import (
	"context"
	"crypto/rand"
	"errors"
	"fmt"
	"gpt-api/framework/auther"
	"gpt-api/framework/codes"
	"gpt-api/framework/ginplus"
	"gpt-api/framework/log"
	"gpt-api/pkg/model"
	"gpt-api/pkg/response"
	"math/big"
	"strconv"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/spf13/viper"
	"github.com/wechatpay-apiv3/wechatpay-go/core"
	"github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers"
	"github.com/wechatpay-apiv3/wechatpay-go/core/downloader"
	"github.com/wechatpay-apiv3/wechatpay-go/core/notify"
	"github.com/wechatpay-apiv3/wechatpay-go/core/option"
	"github.com/wechatpay-apiv3/wechatpay-go/services/payments"
	"github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi"
	"github.com/wechatpay-apiv3/wechatpay-go/utils"
)

// login 登录
func (h *Handler) WeiXinLogin(c *gin.Context) error {
	var req struct {
		Code       string `json:"code" binding:"required"`
		Username   string `json:"username"`
		InviteCode string `json:"invite_code"`
	}
	if err := c.Bind(&req); err != nil {
		return ginplus.ReplyBadRequest(c, err)
	}

	// result
	// {
	// 	openid: "o6x1s5Z1Z1Z1Z1Z1Z1Z1Z1Z1Z1Z1",
	// 	session_key: "o6x1s5Z1Z1Z1Z1Z1Z1Z1Z1Z1Z1",
	// }
	result, err := h.wx.Jscode2session(req.Code)
	if err != nil {
		return ginplus.ReplyError(c, codes.WeixinMiniCodeInvalid, nil)
	}

	// 查询数据库，如果没有账号，则会默认注册。
	//result["openid"] = "ohGwX7dwFBfyz8STkpfxmNk3mSAM"
	user, err := (&model.Users{}).GetUserInfoByOpenId(h.db, result["openid"])
	if err != nil {
		// 如果邀请码存在，查询邀请码用户
		var inviterUserId uint64
		if req.InviteCode != "" {
			inviteUser, err := (&model.Users{}).GetUserInfoByInviteCode(h.db, req.InviteCode)
			if err != nil {
				return ginplus.ReplyInternalError(c, err)
			}

			// 判断邀请人
			if inviteUser.ID == 0 {
				return ginplus.ReplyError(c, codes.TheInvitationCodeIsInvalid, nil)
			}
			user.InviteUserID = uint(inviteUser.ID)
			inviterUserId = uint64(inviteUser.ID)
		}

		user.OpenID = result["openid"]
		err := user.Create(h.db)
		if err != nil {
			return ginplus.ReplyInternalError(c, err)
		}

		// 给邀请用户发放积分
		scoreStr := viper.GetString("upUserGive.score")
		score, err := strconv.Atoi(scoreStr)
		if err != nil {
			return ginplus.ReplyInternalError(c, err)
		}
		if inviterUserId > 0 && score > 0 {
			err := (&model.Score{}).Charge(h.db, int(inviterUserId), user.ID, score)
			if err != nil {
				return ginplus.ReplyInternalError(c, err)
			}
		}

	}

	// 查询是否有成员根级信息,没有根级信息则生成默认信息
	_, err = (&model.Member{}).GetMemberRootByUserId(h.db, user.ID)
	if err != nil {
		member := &model.Member{
			UserID:   uint(user.ID),
			Username: "",
			IsLiving: 1,
		}
		fmt.Println(member)
		err := member.AddOrUpdateMember(h.db, *member)
		if err != nil {
			return ginplus.ReplyInternalError(c, err)
		}
	}

	// 根据用户的ID，生成token
	userInfo := &auther.UserInfo{UserId: user.ID}

	tokenInfo, err := h.auther.GenerateToken(userInfo)
	if err != nil {
		return ginplus.ReplyInternalError(c, err)
	}

	fields := make([]log.Field, 0, 1)
	fields = append(fields, log.String("token", tokenInfo.GetAccessToken()))
	log.Info("[用户登录token]", fields...)
	return ginplus.ReplyJSON(c, tokenInfo)

}

// 微信支付预付单
func (h *Handler) WechatPay(c *gin.Context) error {
	// 验证参数
	userId := ginplus.GetUserID(c)
	//userId := uint64(27)
	var req struct {
		TopupWaysId uint `json:"topup_ways_id"`
		Amount      uint `json:"amount"`
	}
	if err := c.Bind(&req); err != nil {
		return ginplus.ReplyBadRequest(c, err)
	}

	// TopupWaysId或Amount必传其一
	if req.TopupWaysId == 0 && req.Amount == 0 {
		return ginplus.ReplyBadRequest(c, errors.New("TopupWaysId或Amount必传其一"))
	}

	// 查询当前用户信息
	user, err := (&model.Users{}).GetUserInfoById(h.db, userId)
	if err != nil {
		return ginplus.ReplyInternalError(c, err)
	}

	// 如果传TopupWaysId,则查询TopupWay信息,使用price
	var amount uint
	if req.TopupWaysId > 0 {
		topupWay, err := (&model.TopupWays{}).GetTopupWayById(h.db, req.TopupWaysId)
		if err != nil {
			return ginplus.ReplyInternalError(c, err)
		}

		// 单位换算
		amount = uint(topupWay.Price * 100)
	} else {
		amount = req.Amount
	}

	// 判断如果金额0返回错误
	if amount == 0 {
		return ginplus.ReplyBadRequest(c, errors.New("金额不能为0"))
	}

	// 微信支付配置
	var (
		mchID                      string = viper.GetString("wxPay.mchid")
		mchCertificateSerialNumber string = viper.GetString("wxPay.mch_certificate_serial_number")
		mchAPIv3Key                string = viper.GetString("wxPay.mch_api_v3_key")
	)

	// 使用 utils 提供的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
	mchPrivateKey, err := utils.LoadPrivateKeyWithPath(viper.GetString("wxPay.mch_certificate_path"))
	if err != nil {
		return ginplus.ReplyInternalError(c, err)
	}

	ctx := context.Background()

	// 使用商户私钥等初始化 client，并使它具有自动定时获取微信支付平台证书的能力
	opts := []core.ClientOption{
		option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key),
	}
	client, err := core.NewClient(ctx, opts...)
	if err != nil {
		return ginplus.ReplyInternalError(c, err)
	}

	// 生成订单号,随机数
	random, _ := rand.Int(rand.Reader, big.NewInt(899999))
	random = random.Add(random, big.NewInt(100000))

	//拼接时间戳+随机数
	orderNo := strconv.Itoa(int(time.Now().Unix())) + random.String()

	//  JSAPI下单
	svc := jsapi.JsapiApiService{Client: client}

	// 得到prepay_id，以及调起支付所需的参数和签名
	resp, _, err := svc.PrepayWithRequestPayment(ctx,
		jsapi.PrepayRequest{
			Appid:       core.String(viper.GetString("wxPay.appid")),
			Mchid:       core.String(mchID),
			Description: core.String("充值"),
			OutTradeNo:  core.String(orderNo),
			NotifyUrl:   core.String("https://www.bridgememory.net/api/v1/common/wxpayasyn"),
			Amount: &jsapi.Amount{
				Total: core.Int64(int64(amount)),
			},
			Payer: &jsapi.Payer{
				Openid: core.String(user.OpenID),
			},
		},
	)

	if err != nil {
		return ginplus.ReplyInternalError(c, err)
	}

	// 创建待支付记录
	wxpay := &model.WxPay{
		UserID:      userId,
		Type:        "jf",
		TradeType:   "JSAPI",
		TradeState:  "NOTPAY",
		AmountTotal: amount,
		OutTradeNo:  orderNo,
		PrepayId:    *resp.PrepayId,
		NonceStr:    *resp.NonceStr,
		TimeStamp:   *resp.TimeStamp,
		Package:     *resp.Package,
		SignType:    *resp.SignType,
		PaySign:     *resp.PaySign,
	}
	if req.TopupWaysId > 0 {
		wxpay.TopupWaysId = uint64(req.TopupWaysId)
	}
	err = model.WxPay{}.CreateWxPay(h.db, *wxpay)
	if err != nil {
		return ginplus.ReplyInternalError(c, err)
	}

	// 构造微信支付预下单结构
	returnData := &response.WxPayPrepayResp{
		OutTradeNo: orderNo,
		PrepayId:   *resp.PrepayId,
		AppId:      viper.GetString("wxPay.appid"),
		NonceStr:   *resp.NonceStr,
		TimeStamp:  *resp.TimeStamp,
		Package:    *resp.Package,
		SignType:   *resp.SignType,
		PaySign:    *resp.PaySign,
	}

	return ginplus.ReplyJSON(c, returnData)
}

// 微信支付同步回调
func (h *Handler) WechatPaySync(c *gin.Context) error {
	// 验证参数
	userId := ginplus.GetUserID(c)
	var req struct {
		OutTradeNo string `form:"out_trade_no" binding:"required"`
	}
	if err := c.Bind(&req); err != nil {
		return ginplus.ReplyBadRequest(c, err)
	}

	// 查询支付信息
	wxPay, err := (&model.WxPay{}).GetWxPayByOutTradeNo(h.db, req.OutTradeNo)
	if err != nil {
		return ginplus.ReplyInternalError(c, err)
	}
	if wxPay.ID == 0 {
		return ginplus.ReplyError(c, codes.WeixinPayNotFound, nil)
	}
	if wxPay.UserID != userId {
		return ginplus.ReplyError(c, codes.WeixinPayNotFound, nil)
	}

	// 构造返回数据
	var wxPayResp = &response.WxPayResp{
		OutTradeNo:  wxPay.OutTradeNo,
		TradeState:  wxPay.TradeState,
		SuccessTime: "",
	}
	if !wxPay.SuccessTime.IsZero() {
		wxPayResp.SuccessTime = wxPay.SuccessTime.Format("2006-01-02 15:04:05")
	}

	// 返回数据
	if wxPay.TradeState == "SUCCESS" {
		return ginplus.ReplyJSON(c, wxPayResp)
	}

	// 微信支付配置
	var (
		mchID                      string = viper.GetString("wxPay.mchid")
		mchCertificateSerialNumber string = viper.GetString("wxPay.mch_certificate_serial_number")
		mchAPIv3Key                string = viper.GetString("wxPay.mch_api_v3_key")
	)

	// 使用 utils 提供的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
	mchPrivateKey, err := utils.LoadPrivateKeyWithPath(viper.GetString("wxPay.mch_certificate_path"))
	if err != nil {
		return ginplus.ReplyInternalError(c, err)
	}

	ctx := context.Background()

	// 使用商户私钥等初始化 client,并使它具有自动定时获取微信支付平台证书的能力
	opts := []core.ClientOption{
		option.WithWechatPayAutoAuthCipher(mchID, mchCertificateSerialNumber, mchPrivateKey, mchAPIv3Key),
	}
	client, err := core.NewClient(ctx, opts...)
	if err != nil {
		return ginplus.ReplyInternalError(c, err)
	}

	// 查询订单
	svc := jsapi.JsapiApiService{Client: client}
	resp, _, err := svc.QueryOrderByOutTradeNo(ctx,
		jsapi.QueryOrderByOutTradeNoRequest{
			OutTradeNo: core.String(wxPay.OutTradeNo),
			Mchid:      core.String(mchID),
		},
	)
	if err != nil {
		return ginplus.ReplyInternalError(c, err)
	}

	// 判断状态
	if resp != nil && *resp.TradeState == "SUCCESS" {
		// 更新支付状态
		err = wxPay.UpdateWxPaySuccess(h.db, wxPay)
		if err != nil {
			return ginplus.ReplyInternalError(c, err)
		}
	}

	// 返回数据
	wxPayResp.TradeState = *resp.TradeState
	if !wxPay.SuccessTime.IsZero() {
		wxPayResp.SuccessTime = wxPay.SuccessTime.Format("2006-01-02 15:04:05")
	}
	return ginplus.ReplyJSON(c, wxPayResp)
}

// 微信支付异步回调
func (h *Handler) WechatPayAsyn(c *gin.Context) response.WxPayAsynResp {
	// 初始返回构造
	returnData := &response.WxPayAsynResp{Code: "FAIL", Message: "失败"}

	// 微信支付配置
	var (
		mchID                      string = viper.GetString("wxPay.mchid")
		mchCertificateSerialNumber string = viper.GetString("wxPay.mch_certificate_serial_number")
		mchAPIv3Key                string = viper.GetString("wxPay.mch_api_v3_key")
	)

	// 使用 utils 提供的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
	mchPrivateKey, err := utils.LoadPrivateKeyWithPath(viper.GetString("wxPay.mch_certificate_path"))
	if err != nil {
		return *returnData
	}

	ctx := context.Background()
	request := c.Request // 这个值是*http.Request

	// 1. 使用 `RegisterDownloaderWithPrivateKey` 注册下载器
	err = downloader.MgrInstance().RegisterDownloaderWithPrivateKey(ctx, mchPrivateKey, mchCertificateSerialNumber, mchID, mchAPIv3Key)
	if err != nil {
		return *returnData
	}

	// 2. 获取商户号对应的微信支付平台证书访问器
	certificateVisitor := downloader.MgrInstance().GetCertificateVisitor(mchID)
	// 3. 使用证书访问器初始化 `notify.Handler`
	handler := notify.NewNotifyHandler(mchAPIv3Key, verifiers.NewSHA256WithRSAVerifier(certificateVisitor))

	// 验签未通过，或者解密失败
	var notifyReq *notify.Request
	transaction := new(payments.Transaction)
	notifyReq, err = handler.ParseNotifyRequest(ctx, request, transaction)

	// 打印微信头部信息
	//fields := make([]log.Field, 0, 5)
	//fields = append(fields, log.String("Wechatpay-Signature", c.GetHeader("Wechatpay-Signature")))
	//fields = append(fields, log.String("Wechatpay-Timestamp", c.GetHeader("Wechatpay-Timestamp")))
	//fields = append(fields, log.String("Wechatpay-Signature-Type", c.GetHeader("Wechatpay-Signature-Type")))
	//fields = append(fields, log.String("Wechatpay-Serial", c.GetHeader("Wechatpay-Serial")))
	//fields = append(fields, log.String("Wechatpay-Nonce", c.GetHeader("Wechatpay-Nonce")))
	//log.Info("[微信支付回调Handler]", fields...)

	if err != nil {
		fmt.Println("err:", err)
		return *returnData
	}
	if notifyReq.Summary != "支付成功" {
		return *returnData
	}

	// 处理通知内容
	// 交易状态，枚举值：SUCCESS：支付成功REFUND：转入退款NOTPAY：未支付CLOSED：已关闭REVOKED：已撤销（付款码支付）USERPAYING：用户支付中（付款码支付）PAYERROR：支付失败(其他原因，如银行返回失败)
	if *transaction.TradeState != "SUCCESS" {
		return *returnData
	}

	// 查询订单
	wxPay, err := (&model.WxPay{}).GetWxPayByOutTradeNo(h.db, *transaction.OutTradeNo)
	if err != nil {
		return *returnData
	}
	if wxPay.ID == 0 {
		return *returnData
	}
	if wxPay.TradeState == "SUCCESS" {
		returnData.Code = "SUCCESS"
		returnData.Message = "成功"
		return *returnData
	}

	// 支付成功状态操作
	if *transaction.TradeState == "SUCCESS" {
		// 更新支付状态
		err = wxPay.UpdateWxPaySuccess(h.db, wxPay)
		if err != nil {
			return *returnData
		}
	}

	// 通知应答
	returnData.Code = "SUCCESS"
	returnData.Message = "成功"
	return *returnData
}
